package com.dissertation.service.impl;

import com.dissertation.model.Assets;
import com.dissertation.pojo.BaseParam;
import com.dissertation.pojo.Param;
import com.dissertation.pojo.UnwindCollections;
import com.dissertation.service.IAssetsFindByKeyword;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.UnwindOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static org.springframework.data.mongodb.core.query.Criteria.where;

@Service
public class AssetsFindByKeywordImpl implements IAssetsFindByKeyword {

    @Autowired
    MongoTemplate mongoTemplate;


    @Override
    public List<Assets> findByKeyword( String keyword1,
                                       String keyword2,
                                       boolean op ) {

        return op ? findByKeywordAnd( keyword1, keyword2 )
                : findByKeywordOr( keyword1, keyword2 );
    }

    @Override
    public List<Assets> findByKeywordAnd( String keyword1, String keyword2 ) {
        Query query = Query.query(
                new Criteria().andOperator(
                        getCriteria( keyword1 ),
                        getCriteria( keyword2 )
                )
        );
        return mongoTemplate.find( query, Assets.class );
    }

    private Criteria getCriteria( String keyword1 ) {
        return new Criteria().orOperator(
                where( "author" ).regex( keyword1 ),
                where( "name" ).regex( keyword1 ),
                where( "categoryLeafName" ).regex( keyword1 ),
                where( "categoryTreeName" ).regex( keyword1 )
        );
    }


    @Override
    public List<Assets> findByKeywordOr( String keyword1, String keyword2 ) {
        Query query = Query.query(
                new Criteria().orOperator(
                        where( "author" ).regex( keyword1 ),
                        where( "author" ).regex( keyword2),
                        where( "name" ).regex( keyword1 ),
                        where( "name" ).regex( keyword2 ),
                        where( "categoryLeafName" ).regex( keyword1 ),
                        where( "categoryLeafName" ).regex( keyword2 ),
                        where( "categoryTreeName" ).regex( keyword1 ),
                        where( "categoryTreeName" ).regex( keyword2 )
                )
        );
        return mongoTemplate.find( query, Assets.class );
    }

    @Override
    public List<UnwindCollections> findByKeyword(String keyword,String adminID) {
        Criteria criteria = new Criteria().orOperator(
                where("favoriteList.projectName").regex(keyword),
                where("favoriteList.author").regex(keyword),
                where("favoriteList.projectType").regex(keyword),
                where("favoriteList.tag").regex(keyword),
                where("favoriteList.time").regex(keyword)
        );
        criteria.andOperator(where("adminID").is(adminID));
        UnwindOperation unwindOperation = Aggregation.unwind("favoriteList");
        Aggregation aggregation =Aggregation.newAggregation(
                unwindOperation,
                Aggregation.match(criteria)
        );
        AggregationResults<UnwindCollections> aggregationResults = mongoTemplate.aggregate(aggregation,"collections",UnwindCollections.class);
        List<UnwindCollections> collectionsList = new ArrayList<>();
        aggregationResults.getMappedResults().forEach(e->collectionsList.add(e));
        System.out.println(aggregationResults.getMappedResults());
        System.out.println(aggregationResults.getRawResults());
        return collectionsList;
    }

    @Override
    public List<Assets> findByParam(Param param) {
        List<BaseParam> baseParams = param.getBaseParams();
        List<AggregationOperation> criteria = baseParams.stream()
                .map(baseParam -> {
                    Criteria tmp = Criteria.where(baseParam.getName1())
                            .regex(baseParam.getVal1());
                    return Aggregation.match(
                            baseParam.isOperator() ?
                                    tmp.and(baseParam.getName2()).regex(baseParam.getVal2()) : // and
                                    tmp.orOperator(Criteria.where(baseParam.getName2()).regex(baseParam.getVal2()))
                    );
                }).collect(Collectors.toList());
        Aggregation aggregation = Aggregation.newAggregation(criteria);
        return mongoTemplate.aggregate(aggregation, "assets", Assets.class).getMappedResults();
    }
}
